package com.zimu.baiapigateway.filter;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zimu.common.exception.BusinessException;
import com.zimu.common.model.common.ErrorCode;
import com.zimu.common.model.dto.userinterfaceinfo.UserInterfaceInfoUpdateRequest;
import com.zimu.common.model.entity.InterfaceInfo;
import com.zimu.common.model.entity.User;
import com.zimu.common.service.CommonInterfaceInfoService;
import com.zimu.common.service.CommonUserInterfaceInfoService;
import com.zimu.common.service.CommonUserService;
import com.zimu.sdk.utils.EncryptUtil;
import io.netty.util.internal.StringUtil;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;

import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebSession;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
    @DubboReference
    CommonUserService commonUserService;
    @DubboReference
    CommonInterfaceInfoService interfaceInfoService;
    @DubboReference
    CommonUserInterfaceInfoService userInterfaceInfoService;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        HttpHeaders headers = request.getHeaders();
        String accessKey = headers.getFirst("accessKey");
        String signed = headers.getFirst("signed");
        String timeStr = headers.getFirst("time");
        String nonce = headers.getFirst("nonce");
        if (StrUtil.hasBlank(accessKey,signed,timeStr,nonce) )
            return stopMethodAndResponse(response,HttpStatus.BAD_REQUEST);
        Long time = Long.valueOf(timeStr);
        Long now = System.currentTimeMillis();
        if ( now - time < 0 || now - time > 1000*60*5  )
            return stopMethodAndResponse(response,HttpStatus.BAD_REQUEST);
        if (nonce.length() >= 5 )
            return stopMethodAndResponse(response,HttpStatus.BAD_REQUEST);
        String jsonStr = null;
        if (request.getMethod() == HttpMethod.POST) {
            AtomicReference<String> requestBody = new AtomicReference<>("");
            RecorderServerHttpRequestDecorator requestDecorator = new RecorderServerHttpRequestDecorator(request);
            Flux<DataBuffer> body = requestDecorator.getBody();
            body.subscribe(buffer -> {
                CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
                requestBody.set(charBuffer.toString());
            });
            //TODO 拿到body参数之后做你自己的校验
            jsonStr = requestBody.get();
//            System.out.println( "----> Body" + jsonStr);
        }else {
            jsonStr = request.getQueryParams().toSingleValueMap().toString();
        }
        if (StrUtil.isBlank(jsonStr) )
            return stopMethodAndResponse(response,HttpStatus.FORBIDDEN);
        //TODO 这里secretKey要从数据库中读取,通过accessKey取到用户信息读取secretKey
        User user = commonUserService.getUserByAccessKey(accessKey);
        if (user == null)
            return stopMethodAndResponse(response,HttpStatus.FORBIDDEN);
        String secretKey = user.getSecretKey();
        String encrypt = EncryptUtil.encrypt(jsonStr + secretKey);
        if ( !encrypt.equals(signed) )
            throw new RuntimeException("无权限");
        //TODO 查询接口是否存在，不存在的接口不放行
        String url = request.getPath().toString();
        InterfaceInfo interfaceInfo = interfaceInfoService.getOneByUrl(url);
        if (interfaceInfo == null)
            return stopMethodAndResponse(response,HttpStatus.FORBIDDEN);
        UserInterfaceInfoUpdateRequest updateRequest = new UserInterfaceInfoUpdateRequest();
        updateRequest.setUserId(user.getId());
        updateRequest.setInterfaceInfoId(interfaceInfo.getId());
        boolean updateInvokeNum = userInterfaceInfoService.addInvokeNum(updateRequest);
        if (!updateInvokeNum)
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        return chain.filter(exchange);
    }
    @Override
    public int getOrder() {
        return -1;
    }

    private Mono<Void> stopMethodAndResponse(ServerHttpResponse response,HttpStatus httpStatus){
        response.setStatusCode(httpStatus);
        return response.setComplete();

    }
    public class RecorderServerHttpRequestDecorator  extends ServerHttpRequestDecorator {
        private final List<DataBuffer> dataBuffers = new ArrayList<>();

        public RecorderServerHttpRequestDecorator(ServerHttpRequest delegate) {
            super(delegate);
            super.getBody().map(dataBuffer -> {
                dataBuffers.add(dataBuffer);
                return dataBuffer;
            }).subscribe();
        }

        @Override
        public Flux<DataBuffer> getBody() {
            return copy();
        }

        private Flux<DataBuffer> copy() {
            return Flux.fromIterable(dataBuffers)
                    .map(buf -> buf.factory().wrap(buf.asByteBuffer()));
        }
    }
}
